/**********************************************************
 * Copyright © 2024 Walkline Wang (https://walkline.wang)
 * Gitee: https://gitee.com/walkline/keypad-for-ops
 **********************************************************/
#ifndef __CH9329__
#define __CH9329__

#include "Arduino.h"
#include "src/common.h"

#define RETURN_MASK 0x80
#define PACKAGE_LENGTH 6U /* 命令包长度，不包含数据长度 */

#define CH9329_UNKNOWN 0xff
#define CH9329_GET_INFO 0xfe
#define CH9329_SUCCESS 0x00     /* 命令执行成功 */
#define CH9329_ERR_TIMEOUT 0xe1 /* 串口接收一个字节超时 */
#define CH9329_ERR_HEAD 0xe2    /* 串口接收包头字节出错 */
#define CH9329_ERR_CMD 0xe3     /* 串口接收命令码错误 */
#define CH9329_ERR_SUM 0xe4     /* 累加和检验值不匹配 */
#define CH9329_ERR_PARA 0xe5    /* 参数错误 */
#define CH9329_ERR_OPERATE 0xe6 /* 帧正常，执行失败 */

typedef uint8_t ch9329_err_t;

class CH9329 {
  public:
    enum ch9329_command_e : uint8_t {
        CMD_GET_INFO = 0x01,
        CMD_SEND_KB_GENERAL_DATA = 0x02,
        CMD_SEND_KB_MEDIA_DATA = 0x03,
        CMD_SEND_MS_ABS_DATA = 0x04,
        CMD_SEND_MS_REL_DATA = 0x05,
        CMD_SEND_MY_HID_DATA = 0x06,
        CMD_READ_MY_HID_DATA = 0x87,
        CMD_GET_PARA_CFG = 0x08,
        CMD_SET_PARA_CFG = 0x09,
        CMD_GET_USB_STRING = 0x0a,
        CMD_SET_USB_STRING = 0x0b,
        CMD_SET_DEFAULT_CFG = 0x0c,
        CMD_RESET = 0x0f
    };

    struct ch9329_chip_info_t {
        /* 0x30: v1.0，0x31:v1.1，类推 */
        char version[5];

        /* 0x00: 未连接计算机或未识别
         * 0x01: 已连接计算机并识别成功 */
        uint8_t usb_status : 1;

        uint8_t num_lock : 1;
        uint8_t caps_lock : 1;
        uint8_t scroll_lock : 1;
    } chip_info = {};

  private:
    /* { head0, head1, addr } */
    const uint8_t head[3] = {0x57, 0xab, 0};

    /* 从芯片串口接收到的数据 */
    String hid_data = "";

  public:
    /* 初始化外设 */
    void setup(void);

    /* 发送芯片复位命令 */
    void reset(void);

    ch9329_err_t get_chip_info(void);

    ch9329_err_t send_hid_data(uint8_t command);

    ch9329_err_t send_hid_data(uint8_t command, int8_t *data, uint8_t length);

    ch9329_err_t receive_hid_data(void);

  private:
    /* 检查并接收串口数据 */
    void check_hid_data(void);

    void parse_chip_info_data(void);

    uint8_t calculate_checksum(const uint8_t *data, uint8_t lenth);
};

#endif
